/**
 * 
 */
package com.rrd.hive.udf;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;


/**
 * 人行征信上报计算IRR收益率函数
 * 实现excel内部函数IRR，需要传入2个或3个参数：
 * 参数1：期号
 * 参数2：金额
 * 参数3：进件金额
 * 参数4：
 * @author liubaoxin
 *
 */
public class RhzxlIRRUDF extends UDF {
	 private static final Pattern COMMON_PATTERN = Pattern.compile(":");
	public Double evaluate(List<String> list, Double loanAmt) {
	if(list==null || loanAmt==null){
		return null;
	} 
	 return evaluate(list,loanAmt, 0.1D);
	}
	/**
	 * 
	 * @param list，集合元素结构：(期数,金额),期数主要用于排序
	 * @param loanAmt
	 * @param guess
	 * @return
	 */
	public Double evaluate(List<String> list, Double loanAmt, double guess) {
		if(list==null || loanAmt==null){
			return null;
		}
		 sortBy(list);
		 double[] values=parseToDouble(list, loanAmt);
		 
		 double d = irr(values,guess);
		 if(Double.isNaN(d)){
			 d = irr(values,guess/10);
		 }
		 return d;
	}
	private static double[]  parseToDouble(List<String> list,double loanAmt){
		 double[] values=new double[list.size()+1];
		 values[0]=0-loanAmt;
		 for (int i=0;i<list.size();i++){
			 values[i+1]=Double.parseDouble(COMMON_PATTERN.split(list.get(i))[1]);
		 }
		 
		 return values;
	}
	private static void sortBy(List<String> list){
		Collections.sort(list, new Comparator<String>() {

			@Override
			public int compare(String o1, String o2) {
				 
				return o1.compareTo(o2);
			}
			
		});
		
	}
	public static double irr(double[] values, double guess) {
		 final int maxIterationCount = 20;
	        final double absoluteAccuracy = 1E-7;

	        double x0 = guess;
	        double x1;

	        int i = 0;
	        while (i < maxIterationCount) {

	            // the value of the function (NPV) and its derivate can be calculated in the same loop
	            final double factor = 1.0 + x0;
	            int k = 0;
	            double fValue = values[k];
	            double fDerivative = 0;
	            for (double denominator = factor; ++k < values.length; ) {
	                final double value = values[k];
	                fValue += value / denominator;
	                denominator *= factor;
	                fDerivative -= k * value / denominator;
	            }

	            // the essense of the Newton-Raphson Method
	            x1 = x0 - fValue/fDerivative;

	            if (Math.abs(x1 - x0) <= absoluteAccuracy) {
	                return x1;
	            }

	            x0 = x1;
	            ++i;
	        }
	        // maximum number of iterations is exceeded
	        return Double.NaN;
	}
	
	 public static void main(String[] args) {
		 List<String> list = new ArrayList<String>();
		  list.add("20191005:501");
		  sortBy(list);
		  for(String s:list){
			  System.out.println(s);
		  }
		  double[] values=parseToDouble(list, 950);
		  
		  double d = irr(values,0.1d);
		  System.out.println(d);
		    d = irr(values,0.05d);
		  System.out.println(d);
 	}
}
